package pl.vm.domain;

import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;

import pl.vm.domain.exceptions.InvalidStateException;
import pl.vm.domain.interfaces.Classification;
import pl.vm.domain.interfaces.Round;
import pl.vm.domain.interfaces.RoundState;
import pl.vm.domain.interfaces.SeazonLoopback;

/**
 * Class containing data for round of playing.
 * 
 * @author krzysztof.graczyk
 * 
 */
public class RoundImpl implements Round {

	final RoundState OPEN = new RoundOpen(this);
	final RoundState FROZEN = new RoundFrozen(this);
	final RoundState CLOSED = new RoundClosed(this);
	
	/* seazon for wich round was played */
	private SeazonLoopback seazon;
	/* round number in seazon, e.g. 5 */
	private int number;
	/* player statistics in that round */
	private Map<Player, Statistics> statistics = new HashMap<>();
	/* points scored by each player in that round */
	private Map<Player, Double> playersPoints = new HashMap<>();
	private Set<VirtualTeam> teams = new HashSet<>();
	Classification classification;
	
	private RoundState state = null;

	/**
	 * Round should only be created by Seazon
	 * 
	 * @param seazon
	 * @param number
	 */
	RoundImpl(SeazonLoopback seazon, int number) {
		this.seazon = seazon;
		this.number = number;
		this.state = new RoundOpen(this);
		this.classification = new RoundClassification(this);
	}

	/**
	 * Gets the statistics for given player
	 * 
	 * @param player
	 * @return {@link Statistics} returns Statistics for this player in this
	 *         round or null if no Statistics were found
	 */
	public Statistics getPlayerStatistics(Player player) {
		return statistics.get(player);
	}

	/**
	 * Gets points for given player
	 * 
	 * @param player
	 * @return points that player earned in that round
	 */
	public Double getPointsForPlayer(Player player) {
		return playersPoints.get(player);
	}

	public void setPlayerStatistics(Player player, Statistics stats) throws InvalidStateException {
		state.setPlayerStatistics(player, stats);
	}

	public int getNumber() {
		return number;
	}

	public void setNumber(int number) {
		this.number = number;
	}

	
	public void freeze() throws InvalidStateException {
		state.freeze();
	}
	
	/**
	 * Closing round
	 * @throws InvalidStateException 
	 */
	public void close() throws InvalidStateException {
		state.close();
	}

	public boolean addTeam(VirtualTeam team) throws InvalidStateException {
		return state.addTeam(team);
	}

	public boolean changeTeam(VirtualTeam oldTeam, VirtualTeam newTeam) throws InvalidStateException {
		return state.changeTeam(oldTeam, newTeam);
	}
	
	public SeazonLoopback getSeazon() {
		return seazon;
	}

	public Map<Player, Statistics> getStatistics() {
		return statistics;
	}

	public Map<Player, Double> getPlayersPoints() {
		return playersPoints;
	}

	@Override
	public Set<VirtualTeam> getTeams() {
		return teams;
	}
	
	public void setState(RoundState state) {
		this.state = state;
	}

	@Override
	public Classification getClassification() {
		return this.state.getClassification();
	}

	@Override
	public int hashCode() {
		final int prime = 31;
		int result = 1;
		result = prime * result + number;
		result = prime * result + ((seazon == null) ? 0 : seazon.getSeazonId().hashCode());
		return result;
	}

	@Override
	public boolean equals(Object obj) {
		if (this == obj)
			return true;
		if (obj == null)
			return false;
		if (getClass() != obj.getClass())
			return false;
		RoundImpl other = (RoundImpl) obj;
		if (number != other.number)
			return false;
		if (seazon == null) {
			if (other.seazon != null)
				return false;
		} else if (!seazon.getSeazonId().equals(other.seazon.getSeazonId()))
			return false;
		return true;
	}
}
